package com.gimi.cloud.bbp.commonService.redis.impl;


import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gimi.cloud.bbp.common.constant.RedisKeyConstant;
import com.gimi.cloud.bbp.common.utils.JsonUtil;
import com.gimi.cloud.bbp.common.utils.StringUtil;
import com.gimi.cloud.bbp.commonService.redis.RedisService;
import com.gimi.cloud.bbp.dao.dao.sys.SysConfigDao;

/**
 * redis相关操作方法实现类
 * @author  luck
 * @date 2017-09-28
 */
@Service
public class RedisServiceImpl implements RedisService {
    private static Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class);
    /** 60s 有慢sql，超时时间设置长一点*/
    private static final long DEFAULT_WAIT_LOCK_TIME_OUT = 60000;
    /** 80s 有慢sql，超时时间设置长一点*/
    private static final long DEFAULT_EXPIRE = 80;

    private SysConfigDao sysConfigDao;
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    public RedisServiceImpl(SysConfigDao sysConfigDao, RedisTemplate<String, String> redisTemplate) {
        this.sysConfigDao = sysConfigDao;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean set(final String key, final String value) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                connection.set(serializer.serialize(key), serializer.serialize(value));
                return true;
            }
        });
        return result;
    }

    @Override
    public String get(final String key) {
        String result = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                byte[] value = connection.get(serializer.serialize(key));
                return serializer.deserialize(value);
            }
        });
        return result;
    }

    @Override
    public Long del(final String key) {
        Long result = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                return connection.del(serializer.serialize(key));
            }
        });
        return result;
    }

    @Override
    public boolean expire(final String key, long expire) {
        return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    @Override
    public <T> boolean setList(String key, List<T> list) {
        String value = JsonUtil.toJson(list);
        return set(key, value);
    }

    @Override
    public <T> List<T> getList(String key, Class<T> clz) {
        String json = get(key);
        if (json != null) {
            return JsonUtil.jsonToList(json,clz);
        }
        return null;
    }

    @Override
    public long lpush(final String key, Object obj) {
        final String value = JsonUtil.toJson(obj);
        long result = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                long count = connection.lPush(serializer.serialize(key), serializer.serialize(value));
                return count;
            }
        });
        return result;
    }

    @Override
    public long rpush(final String key, Object obj) {
        final String value = JsonUtil.toJson(obj);
        long result = redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                long count = connection.rPush(serializer.serialize(key), serializer.serialize(value));
                return count;
            }
        });
        return result;
    }

    @Override
    public String lpop(final String key) {
        String result = redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
                byte[] res = connection.lPop(serializer.serialize(key));
                return serializer.deserialize(res);
            }
        });
        return result;
    }

    @Override
    public void lock(String key, long timeout) {
        String lockKey = generateLockKey(key);
        long start = System.currentTimeMillis();
        long expires = System.currentTimeMillis() + timeout + 1;
        String expiresStr = String.valueOf(expires);
        try {
            while ((System.currentTimeMillis() - start) < timeout) {
                if (redisTemplate.getConnectionFactory().getConnection().setNX(lockKey.getBytes(), expiresStr.getBytes())) {
                    //暂设置为80s过期，防止异常中断锁未释放
                    redisTemplate.expire(lockKey, DEFAULT_EXPIRE, TimeUnit.SECONDS);
                    if (logger.isDebugEnabled()) {
                        logger.debug("add RedisLock[" + key + "].");
                    }
                    break;
                }
                TimeUnit.SECONDS.sleep(3);
            }
        } catch (Exception e) {
            unlock(lockKey);
        }
    }

    @Override
    public void unlock(String key) {
        String lockKey = generateLockKey(key);
        if (logger.isDebugEnabled()) {
            logger.debug("release RedisLock[" + lockKey + "].");
        }
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        connection.del(lockKey.getBytes());
        connection.close();

    }

    private String generateLockKey(String key) {
        return String.format("LOCK:%s", key);
    }

    public void lock(String key) {
        lock(key, DEFAULT_WAIT_LOCK_TIME_OUT);
    }

    @Override
    public boolean set(final String key,final String value,Long expireTime,TimeUnit timeUnit){
        boolean result=false;
        try {
            ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
            opsForValue.set(key, value);
            redisTemplate.expire(key, expireTime, timeUnit);
            result=true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public JSONObject getJsonObjectByKey(String redisKey) {
        String redisStr = this.get(redisKey);
        if(!JsonUtil.isJSON(redisStr)){
            logger.info("获取JSONObject对象异常，redis中"+redisKey+"对应的数据为："+redisStr+";非有效的JSON字符串");
            return  null;
        }
        return JSONObject.parseObject(redisStr);
    }

    @Override
    public JSONArray getJSONArrayByKey(String redisKey) {
        String redisStr = this.get(redisKey);
        if(!JsonUtil.isJSON(redisStr)){
            logger.info("获取JSONArray对象异常，redis中"+redisKey+"对应的数据为："+redisStr+";非有效的JSON字符串");
            return  null;
        }
        return JSONArray.parseArray(redisStr);
    }

    @Override
    public <T> T getObjectByKey(String redisKey, Class<T> clazz) {
        String redisStr = this.get(redisKey);
        if(!JsonUtil.isJSON(redisStr)){
            logger.info("获取"+clazz.getName()+"对象异常，redis中"+redisKey+"对应的数据为："+redisStr+";非有效的JSON字符串");
            return  null;
        }
        return JSONObject.parseObject(redisStr,clazz);
    }

    @Override
    public void setObject(String redisKey, Object obj){
        if(StringUtil.isBlank(redisKey)){
            logger.info("设置数据到redis中异常，redisKey为空");
            return;
        }
        if(null == obj){
            this.set(redisKey,null);
            return;
        }
        this.set(redisKey, JSON.toJSONString(obj));
    }

    @Override
    public void setObject(String redisKey, Object obj, Long expireTime, TimeUnit timeUnit){
        if(StringUtil.isBlank(redisKey)){
            logger.info("设置数据到redis中异常，redisKey为空");
            return;
        }
        if(null == obj){
            this.set(redisKey,null,expireTime,timeUnit);
            return;
        }
        this.set(redisKey,JSON.toJSONString(obj),expireTime,timeUnit);
    }

    @Override
    public String getConfigByKey(String key) {
        String result = "";
        boolean error = false;
        try {
            result = this.get(RedisKeyConstant.BBP_CONFIG_PREFIX + key);
        }catch (Exception e){
            logger.error("获取redis存储的配置"+key+"异常");
            error = true;
        }
        if(StringUtils.isBlank(result)){
            result = sysConfigDao.getValueByKey(key);
            try {
                if(!StringUtils.isBlank(result) && !error){
                    this.set(key,result);
                }
            }catch (Exception e){
                logger.error("获取数据库存储的配置"+key+"异常");
            }
        }
        return result;
    }

    @Override
    public JSONObject getJSONObjectConfigByKey(String key) {
        String configStr = this.getConfigByKey(key);
        if(!JsonUtil.isJSON(configStr)){
            return null;
        }
        return JSONObject.parseObject(configStr);
    }

    @Override
    public JSONArray getJSONArrayConfigByKey(String key) {
        String configStr = this.getConfigByKey(key);
        if(!JsonUtil.isJSON(configStr)){
            return null;
        }
        return JSONArray.parseArray(configStr);
    }

    @Override
    public Integer getIntegerConfigByKey(String key) {
        String configStr = this.getConfigByKey(key);
        if (!NumberUtils.isCreatable(configStr)) {
            return null;
        }
       return Integer.valueOf(configStr);
    }

    @Override
    public Double getDoubleConfigByKey(String key) {
        String configStr = this.getConfigByKey(key);
        if (!NumberUtils.isCreatable(configStr)) {
            return null;
        }
        return Double.valueOf(configStr);
    }
}
